package org.example.lotterysystem.service.impl;

import io.jsonwebtoken.lang.Collections;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.lotterysystem.common.domain.ActivityDetail;
import org.example.lotterysystem.common.domain.dto.CreateActivityDTO;
import org.example.lotterysystem.common.domain.dto.CreatePrizeByActivityParam;
import org.example.lotterysystem.common.domain.dto.CreateUserByActivityParam;
import org.example.lotterysystem.common.domain.dto.PageDTO;
import org.example.lotterysystem.common.domain.enums.ActivityPrizeStatusEnum;
import org.example.lotterysystem.common.domain.enums.ActivityPrizeTiersEnum;
import org.example.lotterysystem.common.domain.enums.ActivityStatusEnum;
import org.example.lotterysystem.common.domain.enums.ActivityUserStatusEnum;
import org.example.lotterysystem.common.domain.vo.CreateActivityVO;
import org.example.lotterysystem.common.domain.vo.FindActivityListVO;
import org.example.lotterysystem.common.domain.vo.GetActivityDetailVO;
import org.example.lotterysystem.common.errorcode.ErrorCodeConstants;
import org.example.lotterysystem.common.exception.ControllerException;
import org.example.lotterysystem.common.exception.ServiceException;
import org.example.lotterysystem.common.utils.JacksonUtil;
import org.example.lotterysystem.common.utils.RedisUtil;
import org.example.lotterysystem.mapper.*;
import org.example.lotterysystem.mapper.dataobject.ActivityDO;
import org.example.lotterysystem.mapper.dataobject.ActivityPrizeDO;
import org.example.lotterysystem.mapper.dataobject.ActivityUserDO;
import org.example.lotterysystem.mapper.dataobject.PrizeDO;
import org.example.lotterysystem.service.ActivityService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ActivityServiceImpl implements ActivityService {
    @Resource
    ActivityMapper activityMapper;

    @Resource
    ActivityPrizeMapper activityPrizeMapper;

    @Resource
    ActivityUserMapper activityUserMapper;

    @Resource
    UserMapper userMapper;

    @Resource
    PrizeMapper prizeMapper;

    @Resource
    RedisUtil redisUtil;

    /**
     * 活动缓存前置，为了区分业务
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    /**
     * 活动缓存过期时间
     */
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;


    /**
     *创建活动
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 涉及多表
    public CreateActivityVO createActivity(CreateActivityDTO createActivityDTO) {
        //校验活动信息
        checkActivityInfo(createActivityDTO);

        //保存活动信息
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(createActivityDTO.getActivityName());
        activityDO.setDescription(createActivityDTO.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        //保存活动_奖品信息
        List<CreatePrizeByActivityParam> prizeParams = createActivityDTO.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = prizeParams.stream()
                .map(prizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        //保存活动_人员信息
        List<CreateUserByActivityParam> userParams = createActivityDTO.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = userParams.stream()
                .map(userParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);

        //保存到redis
        List<Long> prizeIds = createActivityDTO.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);

        ActivityDetail detailDTO = convertToActivityDetailDTO(activityDO, activityUserDOList,
                prizeDOList, activityPrizeDOList);
        cacheActivity(detailDTO);

        //返回
        CreateActivityVO createActivityVO = new CreateActivityVO();
        createActivityVO.setActivityId(activityDO.getId());
        return createActivityVO;
    }

    /**
     *获取活动列表
     */
    @Override
    public FindActivityListVO findActivityList(PageDTO pageDTO) {
        FindActivityListVO findActivityListVO=new FindActivityListVO();

        //获取总数
        int total=activityMapper.count();

        //获取当前列表
        List<ActivityDO> activityDOList=activityMapper.selectActivityList(pageDTO.offset(),pageDTO.getPageSize());

        findActivityListVO.setTotal(total);
        findActivityListVO.setRecords(
                activityDOList.stream()
                        .map(activityDO -> {
                            FindActivityListVO.ActivityInfo activityInfo=new FindActivityListVO.ActivityInfo();
                            activityInfo.setActivityId(activityDO.getId());
                            activityInfo.setActivityName(activityDO.getActivityName());
                            activityInfo.setDescription(activityDO.getDescription());
                            activityInfo.setValid(activityDO.getStatus().equals("RUNNING"));
                            return activityInfo;
                        })
                        .collect(Collectors.toList())
        );
        return findActivityListVO;
    }

    /**
     *获取活动详情
     */
    @Override
    public GetActivityDetailVO getActivityDetail(Long activityId) {
        if(activityId==null){
            throw new ServiceException(ErrorCodeConstants.GET_ACTIVITY_DETAIL_ERROR);
        }

        //从Redis中获取数据
        ActivityDetail activityDetail=getActivityFromCache(activityId);
        if(activityDetail!=null){
            return getGetActivityDetailVO(activityDetail);
        }

        //Redis中无，从数据库中查询
        ActivityDO activityDO=activityMapper.selectById(activityId);
        List<ActivityPrizeDO> activityPrizeDOList=activityPrizeMapper.selectByActivityId(activityId);
        List<ActivityUserDO> activityUserDOList=activityUserMapper.selectByActivityId(activityId);

        //从活动奖品数据中提取出奖品Id
        List<Long> prizeIds=activityPrizeDOList.stream().
                map(ActivityPrizeDO::getPrizeId).collect(Collectors.toList());
        List<PrizeDO> prizeDOList=prizeMapper.batchSelectByIds(prizeIds);

        ActivityDetail detailDTO = convertToActivityDetailDTO(activityDO,activityUserDOList,prizeDOList,activityPrizeDOList);

        //存入Redis中
        cacheActivity(detailDTO);

        return getGetActivityDetailVO(detailDTO);
    }

    /**
     *更新Redis中的活动信息
     */
    @Override
    public void cacheActivity(Long activityId) {
        if(activityId==null){
            throw new ServiceException(ErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }

        ActivityDO activityDO=activityMapper.selectById(activityId);
        if(activityDO==null){
            throw new ServiceException(ErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }

        List<ActivityPrizeDO> activityPrizeDOList=activityPrizeMapper.selectByActivityId(activityId);

        List<ActivityUserDO> activityUserDOList=activityUserMapper.selectByActivityId(activityId);

        List<Long> prizeIds=activityPrizeDOList.stream().
                map(ActivityPrizeDO::getPrizeId).collect(Collectors.toList());
        List<PrizeDO> prizeDOList=prizeMapper.batchSelectByIds(prizeIds);

        //存入Redis中
        cacheActivity(convertToActivityDetailDTO(activityDO,activityUserDOList,prizeDOList,activityPrizeDOList));
    }

    private GetActivityDetailVO getGetActivityDetailVO(ActivityDetail activityDetail) {
        if (null == activityDetail) {
            throw new ControllerException(ErrorCodeConstants.GET_ACTIVITY_DETAIL_ERROR);
        }

        GetActivityDetailVO result = new GetActivityDetailVO();
        result.setActivityId(activityDetail.getActivityId());
        result.setActivityName(activityDetail.getActivityName());
        result.setDescription(activityDetail.getDesc());
        result.setValid(activityDetail.valid());
        // 抽奖顺序：一等奖、二、三
        result.setPrizes(
                activityDetail.getPrizeDTOList().stream()
                        .sorted(Comparator.comparingInt(prizeDTO -> prizeDTO.getTiers().getCode()))
                        .map(prizeDTO -> {
                            GetActivityDetailVO.Prize prize = new GetActivityDetailVO.Prize();
                            prize.setPrizeId(prizeDTO.getPrizeId());
                            prize.setName(prizeDTO.getName());
                            prize.setImageUrl(prizeDTO.getImageUrl());
                            prize.setPrice(prizeDTO.getPrice());
                            prize.setDescription(prizeDTO.getDescription());
                            prize.setPrizeTierName(prizeDTO.getTiers().getMessage());
                            prize.setPrizeAmount(prizeDTO.getPrizeAmount());
                            prize.setValid(prizeDTO.valid());
                            return prize;
                        }).collect(Collectors.toList())
        );
        result.setUsers(
                activityDetail.getUserDTOList().stream()
                        .map(userDTO -> {
                            GetActivityDetailVO.User user = new GetActivityDetailVO.User();
                            user.setUserId(userDTO.getUserId());
                            user.setUserName(userDTO.getUserName());
                            user.setValid(userDTO.valid());
                            return user;
                        }).collect(Collectors.toList())
        );
        return result;
    }

    //缓存完整活动信息
    private void cacheActivity(ActivityDetail detailDTO) {
        // key: ACTIVITY_
        // value: ActivityDetailDTO(json)
        if (null == detailDTO || null == detailDTO.getActivityId()) {
            log.warn("要缓存的活动信息不存在!");
            return;
        }

        try {
            redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    ACTIVITY_TIMEOUT);
        } catch (Exception e) {
            log.error("缓存活动异常，ActivityDetailDTO={}",
                    JacksonUtil.writeValueAsString(detailDTO),
                    e);
        }
    }

    //根据activityId从缓存中查询活动信息
    private ActivityDetail getActivityFromCache(Long activityId){
        if (null == activityId) {
            log.warn("获取缓存活动数据的activityId为空！");
            return null;
        }
        try {
            String str = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if (!StringUtils.hasText(str)) {
                log.info("获取的缓存活动数据为空！key={}", ACTIVITY_PREFIX + activityId);
                return null;
            }
            return JacksonUtil.readValue(str, ActivityDetail.class);
        } catch (Exception e) {
            log.error("从缓存中获取活动信息异常，key={}", ACTIVITY_PREFIX + activityId, e);
            return null;
        }
    }

    private ActivityDetail convertToActivityDetailDTO(ActivityDO activityDO,
                                                      List<ActivityUserDO> activityUserDOList,
                                                      List<PrizeDO> prizeDOList,
                                                      List<ActivityPrizeDO> activityPrizeDOList) {
        ActivityDetail detailDTO = new ActivityDetail();
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDesc(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

        List<ActivityDetail.PrizeDTO> prizeDTOList = activityPrizeDOList
                .stream()
                .map(apDO -> {
                    ActivityDetail.PrizeDTO prizeDTO = new ActivityDetail.PrizeDTO();
                    prizeDTO.setPrizeId(apDO.getPrizeId());
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    // 如果PrizeDO为空，不执行当前方法，不为空才执行
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());
        detailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetail.UserDTO> userDTOList = activityUserDOList.stream()
                .map(auDO -> {
                    ActivityDetail.UserDTO userDTO = new ActivityDetail.UserDTO();
                    userDTO.setUserId(auDO.getUserId());
                    userDTO.setUserName(auDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());
        detailDTO.setUserDTOList(userDTOList);

        return detailDTO;
    }


    private void checkActivityInfo(CreateActivityDTO createActivityDTO) {
        //判断信息是否为空
        if(createActivityDTO==null){
            throw new ServiceException(ErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }
        //人是否存在
        List<Long> userIds=createActivityDTO.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existUserIds = userMapper.selectExistByIds(userIds);
        if(Collections.isEmpty(existUserIds)){
            throw new ServiceException(ErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id -> {
            if (!existUserIds.contains(id)) {
                throw new ServiceException(ErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });
        //奖品是否存在
        List<Long> prizeIds=createActivityDTO.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPrizeIds=prizeMapper.selectExistByIds(prizeIds);
        if(Collections.isEmpty(existPrizeIds)){
            throw new ServiceException(ErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id->{
            if(!existPrizeIds.contains(id)){
                throw new ServiceException(ErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });
        //人员数量必须大于等于奖品数量
        int userAmount = createActivityDTO.getActivityUserList().size();
        long prizeAmount = createActivityDTO.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if (userAmount < prizeAmount) {
            throw new ServiceException(ErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }

        createActivityDTO.getActivityPrizeList().forEach(prize -> {
            if (null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
        //校验奖品等级有效
        createActivityDTO.getActivityPrizeList().forEach(prize -> {
            if (null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }
}
